﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using TaskRecall.Data;

namespace TaskRecall
{
    public partial class MainForm : Form
    {
        Storage data;
        FilterSelection<Task> taskSelection;
        FilterSelection<Category> categorySelection;

        public MainForm() {
            InitializeComponent();

            data = Storage.Load("D:\\Alex\\Programs\\TaskRecall\\data.xml");
            taskSelection = new FilterSelection<Task>(data);
            categorySelection = new FilterSelection<Category>(data);
            categorySelection.OnAdd += OnCategoryAdd;
            categorySelection.OnRemove += OnCategoryRemove;
            categorySelection.OnItemChange += OnCategoryChange;
            categorySelection.Comparer = new CategoryComparer();

            categorySelection.UpdateSelection();
        }

        #region Category Management
        TreeNode catNode(Category category) {
            if (category == null) {
                return null;
            }
            foreach (TreeNode node in categoriesTreeView.Nodes.Find(category.Name, true)) {
                if (node.Tag == category) {
                    return node;
                }
            }
            return null;
        }

        Category cat(TreeNode node) {
            return ((Category)node.Tag);
        }

        void OnCategoryAdd(Category category) {
            TreeNode parentNode = catNode(category.Parent);
            TreeNodeCollection nodes;
            if (parentNode != null) {
                nodes = parentNode.Nodes;
            } else {
                nodes = categoriesTreeView.Nodes;
            }
            TreeNode node = nodes.Add(category.Name);
            node.Name = category.Name;
            node.Tag = category;
        }

        void OnCategoryRemove(Category category) {
            TreeNode node = catNode(category);
            node.Remove();
        }

        void OnCategoryChange(Category category) {
            TreeNode node = catNode(category);
            if (node.Name != category.Name) {
                node.Text = category.Name;
                node.Name = category.Name;
            }
            Category parentInTree = node.Parent != null ? (Category)node.Parent.Tag : null;
            if (parentInTree != category.Parent) {
                node.Remove();
                TreeNode newParentNode = catNode(category.Parent);
                if (newParentNode == null) {
                    categoriesTreeView.Nodes.Add(node);
                } else {
                    newParentNode.Nodes.Add(node);
                }
            }
        }

        void CategoriesTreeViewMouseDown(object sender, MouseEventArgs e) {
            categoriesTreeView.SelectedNode = categoriesTreeView.GetNodeAt(e.X, e.Y);
        }
        void CategoriesTreeViewAfterLabelEdit(object sender, NodeLabelEditEventArgs e) {
            if (e.Label != null) {
                e.Node.Name = e.Label;
                cat(e.Node).Name = e.Label;
                data.Save();
            }
        }
        void CategoriesTreeViewBeforeLabelEdit(object sender, NodeLabelEditEventArgs e) {
            e.CancelEdit = e.Node.Tag == data.AllCategory || e.Node.Tag == data.NoneCategory;
        }

        void ContextMenuStripCategoriesOpening(object sender, System.ComponentModel.CancelEventArgs e) {
            if (categoriesTreeView.SelectedNode == null) {
                newCategoryToolStripMenuItem.Enabled = true;
                deleteCategoryToolStripMenuItem.Enabled = false;
            } else {
                bool canManipulate = !data.IsSysCategory(categoriesTreeView.SelectedNode.Tag);
                newCategoryToolStripMenuItem.Enabled = canManipulate;
                deleteCategoryToolStripMenuItem.Enabled = canManipulate;
            }
        }

        void NewCategoryToolStripMenuItemClick(object sender, EventArgs e) {
            Category cat = new Category("New Category");
            if (categoriesTreeView.SelectedNode != null) {
                cat.Parent = (Category)categoriesTreeView.SelectedNode.Tag;
            }
            data.Add(cat);
            ReindexCategories();
            data.Save();
            if (categoriesTreeView.SelectedNode != null) {
                categoriesTreeView.SelectedNode.Expand();
            }
            TreeNode node = catNode(cat);
            categoriesTreeView.SelectedNode = node;
            node.BeginEdit();
        }

        void DeleteCategoryToolStripMenuItemClick(object sender, EventArgs e) {
            TreeNode node = categoriesTreeView.SelectedNode;
            if (node != null) {
                DialogResult res = MessageBox.Show("Delete category \"" + node.Text + "\"? (no tasks will be deleted)",
                        "Confirm Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
                if (res == DialogResult.Yes) {
                    data.Remove((Category)node.Tag);
                    ReindexCategories();
                    data.Save();
                }
            }
        }

        private void categoriesTreeView_ItemDrag(object sender, ItemDragEventArgs e) {
            if (((TreeNode)e.Item).Tag != data.AllCategory && ((TreeNode)e.Item).Tag != data.NoneCategory) {
                DoDragDrop(e.Item, DragDropEffects.Move);
            }
        }

        private void categoriesTreeView_DragEnter(object sender, DragEventArgs e) {
            e.Effect = DragDropEffects.Move;
        }

        private void categoriesTreeView_DragDrop(object sender, DragEventArgs e) {
            bool changed = selectNodeAction((TreeView)sender, e,
                (treeView, target, dragged) => {
                    if (acceptChildCategory(target, dragged)) {
                        cat(dragged).Parent = (Category) target.Tag;
                        target.Expand();
                        return true;
                    }
                    return false;
                },
                (treeView, target, dragged) => {
                    if (acceptChildCategory(target.Parent, dragged) && !data.IsSysCategory(target.Tag)) {
                        dragged.Remove();
                        if (target.Parent == null) {
                            treeView.Nodes.Insert(target.Index, dragged);
                        } else {
                            target.Parent.Nodes.Insert(target.Index, dragged);
                            target.Expand();
                        }
                        cat(dragged).Parent = target.Parent == null ? null : cat(target.Parent);
                        return true;
                    }
                    return false;
                },
                (treeView, target, dragged) => {
                    if (acceptChildCategory(target.Parent, dragged) && !data.IsSysCategory(target.Tag)) {
                        dragged.Remove();
                        if (target.Parent == null) {
                            treeView.Nodes.Insert(target.Index + 1, dragged);
                            ((Category)dragged.Tag).Parent = null;
                        } else {
                            target.Parent.Nodes.Insert(target.Index + 1, dragged);
                            target.Expand();
                        }
                        cat(dragged).Parent = target.Parent == null ? null : cat(target.Parent);
                        return true;
                    }
                    return false;
                }
            );
            if (changed) {
                ReindexCategories();
                data.Save();
            }
        }

        private void categoriesTreeView_DragOver(object sender, DragEventArgs e) {
            selectNodeAction((TreeView)sender, e,
                (treeView, target, dragged) => {
                    if (target != null && acceptChildCategory(target, dragged)) {
                        e.Effect = DragDropEffects.Move;
                        treeView.SelectedNode = target;
                    } else {
                        e.Effect = DragDropEffects.None;
                    }
                    return false;
                },
                (treeView, target, dragged) => {
                    if (acceptChildCategory(target.Parent, dragged) && !data.IsSysCategory(target.Tag)) {
                        e.Effect = DragDropEffects.Move;
                        treeView.SelectedNode = null;
                    } else {
                        e.Effect = DragDropEffects.None;
                    }
                    return false;
                },
                (treeView, target, dragged) => {
                    if (acceptChildCategory(target.Parent, dragged) && !data.IsSysCategory(target.Tag)) {
                        e.Effect = DragDropEffects.Move;
                        treeView.SelectedNode = null;
                    } else {
                        e.Effect = DragDropEffects.None;
                    }
                    return false;
                });
        }

        private delegate bool NodeAction(TreeView treeView, TreeNode target, TreeNode dragged);
        private bool selectNodeAction(TreeView sender, DragEventArgs e, NodeAction inside, NodeAction above, NodeAction below) {
            if (e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false)) {
                Point ptTarget = ((TreeView)sender).PointToClient(new Point(e.X, e.Y));
                Point ptAbove = new Point(ptTarget.X, ptTarget.Y - sender.ItemHeight / 4);
                Point ptBelow = new Point(ptTarget.X, ptTarget.Y + sender.ItemHeight / 4);
                TreeNode targetNode = ((TreeView)sender).GetNodeAt(ptTarget);
                TreeNode aboveNode = ((TreeView)sender).GetNodeAt(ptAbove);
                TreeNode belowNode = ((TreeView)sender).GetNodeAt(ptBelow);
                TreeNode movedNode = (TreeNode)e.Data.GetData("System.Windows.Forms.TreeNode");
                if (targetNode == null && belowNode != null) {
                    targetNode = belowNode;
                }
                if (targetNode == null && aboveNode != null) {
                    targetNode = aboveNode;
                }
                if (targetNode == aboveNode && targetNode == belowNode) {
                    return inside(sender, targetNode, movedNode);
                } else if (targetNode == aboveNode && targetNode != belowNode) {
                    return below(sender, targetNode, movedNode);
                } else if (targetNode != aboveNode && targetNode == belowNode) {
                    return above(sender, targetNode, movedNode);
                }
            }
            return false;
        }

        private bool acceptChildCategory(TreeNode target, TreeNode item) {
            return target == null 
                || target.Tag != item.Tag 
                    && !((Category) target.Tag).IsChildOf((Category) item.Tag) 
                    && !data.IsSysCategory(target.Tag);
        }

        private void ReindexCategories() {
            int i = 0;
            ReindexCategories(categoriesTreeView.Nodes, ref i);
        }

        private void ReindexCategories(TreeNodeCollection nodes, ref int i) {
            foreach (TreeNode node in nodes) {
                ((Category)node.Tag).Order = i;
                i++;
                ReindexCategories(node.Nodes, ref i);
            }
        }

        private class CategoryComparer : IComparer<Category> 
        {
            public int Compare(Category x, Category y) {
                return x.Order > y.Order ? 1 : x.Order < y.Order ? -1 : 0;
            }
        }
        #endregion

        #region Task Management

        #endregion
    }
}
